#include <dspSimulator/instruction/ldndw.h>

bool LDNDW::checkCondition(std::shared_ptr<DspMachine> dspMachine) {
    if (isCompactInstruction) {
        return true;
    } else {
        return checkStandardCondition(dspMachine);
    }
}

void LDNDW::loadInstIRIntoJit(llvm::LLVMContext *llvmContext, llvm::Module *module,
                              std::shared_ptr<llvm::IRBuilder<>> irBuilder) {

}

std::shared_ptr<Instruction> LDNDW::decode(std::shared_ptr<InstInfo> instInfo, u64 baseCycle) {
    std::shared_ptr<LDNDW> ldndw;
    auto dspInstInfo = std::dynamic_pointer_cast<DspInstInfo>(instInfo);
    bool isStandardInst = dspInstInfo->isStandardInstruction();
    if (isStandardInst) {
        auto data = vectorToBitSet<32>(dspInstInfo->getBits());
        if (checkBits<5>(data, 2, 0b01001) && checkBits<1>(data, 8, 0b1) && existStandardCondition(data)) {
            ldndw = std::make_shared<LDNDW>(dspInstInfo->getAddress(), baseCycle);
            ldndw->creg_z = extractBits<4>(data, 28);
            ldndw->y = data[7];
            if (ldndw->y == 0) {
                ldndw->funcUnit = FuncUnit::D1;
            } else {
                ldndw->funcUnit = FuncUnit::D2;
            }
            ldndw->mode = extractBits<4>(data, 9).to_ulong();
            ldndw->offsetR = extractBits<5>(data, 13).to_ulong();
            ldndw->cst = ldndw->offsetR;
            ldndw->baseR = extractBits<5>(data, 18).to_ulong();
            ldndw->dst = extractBits<4>(data, 24).to_ulong() << 1;
            ldndw->s = data[1];
            ldndw->p = data[0];
            auto sc = data[23];
            ldndw->instFormat = InstFormat::Kind1;
            if (sc == 0) {
                ldndw->instFormat = InstFormat::Kind2;
            }
        }
    }
    return ldndw;
}

void LDNDW::executeCustom(std::shared_ptr<TargetMachine> targetMachine, ExecutePhase executePhase) {
    auto dspMachine = std::dynamic_pointer_cast<DspMachine>(targetMachine);
    if (executePhase == ExecutePhase::E1) {
        if (instFormat == InstFormat::Kind1) {
            u32 *baseRReadPtr;
            u32 *baseRWritePtr;
            if (y == 0) {
                baseRReadPtr = dspMachine->getSaveRegs()->getReg32ByName(RegAName[baseR])->getDataPtr();
                baseRWritePtr = dspMachine->getRegisters()->getReg32ByName(RegAName[baseR])->getDataPtr();
            } else {
                baseRReadPtr = dspMachine->getSaveRegs()->getReg32ByName(RegBName[baseR])->getDataPtr();
                baseRWritePtr = dspMachine->getRegisters()->getReg32ByName(RegBName[baseR])->getDataPtr();
            }
            m_visitMemAddr = computeRealAddress(baseRReadPtr, baseRWritePtr, mode, 3, dspMachine);
        } else if (instFormat == InstFormat::Kind2) {
            u32 *baseRReadPtr;
            u32 *baseRWritePtr;
            if (y == 0) {
                baseRReadPtr = dspMachine->getSaveRegs()->getReg32ByName(RegAName[baseR])->getDataPtr();
                baseRWritePtr = dspMachine->getRegisters()->getReg32ByName(RegAName[baseR])->getDataPtr();
            } else {
                baseRReadPtr = dspMachine->getSaveRegs()->getReg32ByName(RegBName[baseR])->getDataPtr();
                baseRWritePtr = dspMachine->getRegisters()->getReg32ByName(RegBName[baseR])->getDataPtr();
            }
            m_visitMemAddr = computeRealAddress(baseRReadPtr, baseRWritePtr, mode, 0, dspMachine);
        }
    } else if (executePhase == ExecutePhase::E3) {
        m_dataLo = dspMachine->getMMU()->getMemory()->readNBytes(m_visitMemAddr, 4);
        m_dataHi = dspMachine->getMMU()->getMemory()->readNBytes(m_visitMemAddr + 4, 4);
    } else if (executePhase == ExecutePhase::E5) {
        *dstLoRegPtr = vectorToBitSet<32>(m_dataLo).to_ulong();
        *dstHiRegPtr = vectorToBitSet<32>(m_dataHi).to_ulong();
    }
}

std::string LDNDW::toString() const {
    std::string res;
    if (isCompactInstruction) {

    } else {
        res += getStandardConditionString() + " ";
    }
    res += m_name + " " + FuncUnitName[static_cast<u32>(funcUnit)] + " ";
    switch (instFormat) {
        case InstFormat::Kind1:
            res += addressModeToString() + "," + getDstReg64Name();
            break;
        case InstFormat::Kind2:
            if (y == 0) {
                res = res + " B14[" + std::to_string(cst) + "]," + getDstReg64Name();
            } else {
                res = res + " B15[" + std::to_string(cst) + "]," + getDstReg64Name();
            }
            break;
        default:
            break;
    }
    return res;
}

std::shared_ptr<DspInstruction> LDNDW::clone() {
    return std::make_shared<LDNDW>(*this);
}
